#pragma once

#include <iostream>
#include <vector>
#include <assert.h>
#include <semaphore.h>

template <class T>
class RingQueue
{
    static const int gmaxsize = 10;

public:
    RingQueue(int cap = gmaxsize)
        :_q(cap), _cap(cap)
        {
            _comsumerStep = _producerStep = 0;
            sem_init(&_spaceSem,0,cap);
            sem_init(&_dataSem,0,0);
            pthread_mutex_init(&_cmutex, nullptr);
            pthread_mutex_init(&_pmutex, nullptr);
        }

    ~RingQueue()
    {
        sem_destroy(&_spaceSem);
        sem_destroy(&_dataSem);
        pthread_mutex_destroy(&_cmutex);
        pthread_mutex_destroy(&_pmutex);
    }

    void Push(const T& in)
    {
        P(_spaceSem);
        pthread_mutex_lock(&_pmutex);
        _q[_producerStep++] = in;
        _producerStep %= _cap;
        V(_dataSem);
        pthread_mutex_unlock(&_pmutex);
    }

    void Pop(T* out)
    {
        P(_dataSem);
        pthread_mutex_lock(&_cmutex);
        *out = _q[_comsumerStep++];
        _comsumerStep %= _cap;
        V(_spaceSem);
        pthread_mutex_unlock(&_cmutex);
    }

private:
    void P(sem_t& sem)
    {
        int n = sem_wait(&sem);
        assert(n == 0);
        (void)n;
    }

    void V(sem_t& sem)
    {
        int n = sem_post(&sem);
        assert(n == 0);
        (void)n;
    }

private:
    std::vector<T> _q;
    size_t _cap;
    size_t _comsumerStep;
    size_t _producerStep;
    sem_t _spaceSem;
    sem_t _dataSem;
    pthread_mutex_t _cmutex;
    pthread_mutex_t _pmutex;
};